package com.musichord.core.helper;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.musichord.core.unit.chord.Chord;
import com.musichord.core.unit.chord.PitchPattern;
import com.musichord.core.unit.chord.TexturePattern;
import com.musichord.core.unit.note.Note;

public class StringParser {

	private StringParser() {

	}

	private static StringParser _instance = new StringParser();

	public static final HashMap<String, Integer> NoteMap = new HashMap<String, Integer>();
	static {
		NoteMap.put("C", 60);
		NoteMap.put("D", 62);
		NoteMap.put("E", 64);
		NoteMap.put("F", 65);
		NoteMap.put("G", 67);
		NoteMap.put("A", 69);
		NoteMap.put("B", 71);
	}

	public static Map<String, PitchPattern> PitchPatternMap = new HashMap<String, PitchPattern>();

	/* Regular Expression to parse Note and Note String */
	private static String NoteRegEx = "([#bx\"]?)([CDEFGABcdefgab])([1234]?)";
	private static String NoteStringRegEx = "([#bx\"]?)([CDEFGABcdefgab])([1234]?)([-]*)";

	/* Regular Expression to parse Chord and Chord String */
	private static String ChordRegEx = "([#bx\"]?)([CDEFGAB])";
	static {
		/* Third Chord */
		PitchPatternMap.put("", new PitchPattern(new int[] { 0, 4, 7 }));
		PitchPatternMap.put("maj", new PitchPattern(new int[] { 0, 4, 7 }));
		PitchPatternMap.put("m", new PitchPattern(new int[] { 0, 3, 7 }));
		PitchPatternMap.put("aug", new PitchPattern(new int[] { 0, 4, 8 }));
		// ChordPatternMap.put("+", new int[] { 0, 4, 8 });
		PitchPatternMap.put("dim", new PitchPattern(new int[] { 0, 3, 6 }));
		// ChordPatternMap.put("-", new int[] { 0, 3, 6 });
		PitchPatternMap.put("sus4", new PitchPattern(new int[] { 0, 5, 7 }));
		PitchPatternMap.put("6", new PitchPattern(new int[] { 4, 7, 12 }));
		PitchPatternMap.put("m6", new PitchPattern(new int[] { 3, 7, 12 }));
		PitchPatternMap.put("46", new PitchPattern(new int[] { 7, 12, 16 }));
		PitchPatternMap.put("m46", new PitchPattern(new int[] { 7, 12, 15 }));

		/* Seventh Chord */
		PitchPatternMap
				.put("maj7", new PitchPattern(new int[] { 0, 4, 7, 11 }));
		PitchPatternMap.put("m7", new PitchPattern(new int[] { 0, 3, 7, 10 }));
		PitchPatternMap.put("7", new PitchPattern(new int[] { 0, 4, 7, 10 }));
		PitchPatternMap
				.put("m7-5", new PitchPattern(new int[] { 0, 3, 6, 10 }));
		PitchPatternMap.put("dim7", new PitchPattern(new int[] { 0, 3, 6, 9 }));

		ChordRegEx += "((";
		Object[] types = PitchPatternMap.keySet().toArray();
		int i;
		for (i = 0; i < types.length - 1; i++) {
			if (!((String) types[i]).isEmpty()) {
				if (types[i].equals("+")) {
					ChordRegEx += "\\";
				}
				ChordRegEx += types[i];
				ChordRegEx += "|";
			}
		}
		ChordRegEx += types[i];
		ChordRegEx += ")?)";
	}
	private static String ChordStringRegEx = ChordRegEx + "([-]*)";

	public static StringParser getInstance() {
		return _instance;
	}

	public static int ParsePitchNumber(String pitchName) {
		Pattern pattern = Pattern.compile(NoteRegEx);
		Matcher matcher = pattern.matcher(pitchName);
		matcher.find();
		return getPitchNumber(matcher.group(1), matcher.group(2),
				matcher.group(3));
	}

	public static List<Note> ParseNotes(String noteString) {
		Pattern pattern = Pattern.compile(NoteStringRegEx);
		Matcher matcher = pattern.matcher(noteString);
		List<Note> resultList = new Vector<Note>();

		while (matcher.find()) {
			int pitch_number = getPitchNumber(matcher.group(1),
					matcher.group(2), matcher.group(3));
			int duration = (matcher.group(4).length() + 1)
					* ChordHelper.NOTE_UNIT_DURATION;
			resultList.add(new Note(pitch_number, MidiInfo.DEFAULT_VELOCITY,
					duration));
		}

		return resultList;
	}

	public static List<Chord> ParseChords(String chordString) {
		Pattern pattern = Pattern.compile(ChordStringRegEx);
		Matcher matcher = pattern.matcher(chordString);
		List<Chord> resultList = new Vector<Chord>();

		Chord lastChord = null;
		while (matcher.find()) {
			int root_pitch = getPitchNumber(matcher.group(1), matcher.group(2),
					"");
			PitchPattern pitchPattern = PitchPatternMap.get(matcher.group(3));
			int duration = (matcher.group(5).length() + 1)
					* ChordHelper.CHORD_UNIT_DURATION;
			Chord chord = new Chord(root_pitch, duration, pitchPattern,
					new TexturePattern());

			ChordHelper.AdjustChord(lastChord, chord);
			lastChord = chord;

			resultList.add(chord);
		}
		return resultList;
	}

	public static int getPitchNumber(String decorator, String pitchLetter,
			String setNumberString) {
		int setNumber = setNumberString.isEmpty() ? 0 : Integer
				.valueOf(setNumberString);
		int pitchNumber = 60;

		if (NoteMap.containsKey(pitchLetter)) { // upper case
			pitchNumber = NoteMap.get(pitchLetter) - 24;
			if (setNumber == 1 || setNumber == 2) {
				pitchNumber -= setNumber * 12;
			}
		} else {
			pitchNumber = NoteMap.get(pitchLetter.toUpperCase()) - 12;
			pitchNumber += setNumber * 12;
		}

		if (decorator.equals("#")) {
			pitchNumber++;
		} else if (decorator.equals("b")) {
			pitchNumber--;
		} else if (decorator.equals("x")) {
			pitchNumber += 2;
		} else if (decorator.equals("\"")) {
			pitchNumber -= 2;
		}

		return pitchNumber;
	}

}
